﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace WPFSearch.Helpers
{
    public class ObjectReference : MarkupExtension
    {
        public ObjectReference() { }

        public ObjectReference(object key)
        {
            Key = key;

            if (Key == null)
            {
                throw new InvalidOperationException("The Key has not been specified for the ObjectReference.");
            }
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            if (service == null) return this;
            if (service.TargetObject.GetType().Name.EndsWith("SharedDp")) return this;

            var element = GetElement(service.TargetObject);
            if (element == null) return this;

            var window = Window.GetWindow(element);
            if (window == null) return this;

            var result = Key;

            References[Key] = new WeakReference(service.TargetObject);

            WeakReference targetReference;
            if (References.TryGetValue(Key, out targetReference))
            {
                result = targetReference.Target;
            }

            return result;
        }

        private static DependencyObject GetElement(object targetObject)
        {
            if (targetObject is FrameworkContentElement)
            {
                return (FrameworkContentElement)targetObject;
            }

            if (targetObject is Control)
            {
                return (Control)targetObject;
            }

            return null;
        }


        #region Declaration Attached Property

        public static readonly DependencyProperty DeclarationProperty =
            DependencyProperty.RegisterAttached("Declaration", typeof(object), typeof(ObjectReference),
                new FrameworkPropertyMetadata(null));

        public static object GetDeclaration(DependencyObject d)
        {
            return d.GetValue(DeclarationProperty);
        }

        public static void SetDeclaration(DependencyObject d, object value)
        {
            d.SetValue(DeclarationProperty, value);
        }

        #endregion

        [ThreadStatic]
        private static Dictionary<object, WeakReference> _references;

        private static Dictionary<object, WeakReference> References
        {
            get { return _references ?? (_references = new Dictionary<object, WeakReference>()); }
        }

        public object Key { get; set; }

        public object GetTarget(object key)
        {
            WeakReference targetReference;

            if (References.TryGetValue(Key, out targetReference))
            {
                return targetReference.Target;
            }

            return null;
        }
    }
}
